package hxgo

import (
	"html/template"
	"path"
	"bytes"
)

// view object
type View struct {
	dir     string
	IsCache bool
	templates map[string]*template.Template
	funcMap template.FuncMap
}

// set view dir
func (this *View) SetDir(dir string) {
	this.dir = dir
}

// get view files for rendering
func (this *View) getFiles(tpl string, nested []string) []string {
	files := make([]string, 0)
	files = append(files, path.Join(this.dir, tpl))
	if nested != nil {
		for _, nest := range nested {
			files = append(files, path.Join(this.dir, nest))
		}
	}
	return files
}

// get new *template.Template
func (this *View) newTpl(key string, name string, files ...string) (*template.Template, error) {
	t := template.New(name)
	t = t.Funcs(this.funcMap)
	t, e := t.ParseFiles(files...)
	if e != nil {
		return nil, e
	}
	if this.IsCache {
		this.templates[key] = t
	}
	return t, nil
}

// create new *template.Template
func (this *View) Create(tpl string, nested []string) (*template.Template, error) {
	keyName := path.Join(this.dir, tpl)
	var tp *template.Template
	if this.IsCache {
		tp = this.templates[keyName]
		if tp != nil {
			return tp, nil
		}
	}
	files := this.getFiles(tpl, nested)
	var e error
	tp, e = this.newTpl(keyName, path.Base(keyName), files...)
	if e != nil {
		return nil, e
	}
	if this.IsCache {
		this.templates[keyName] = tp
	}
	return tp, nil
}

// get cached *template.Template
func (this *View) Get(tpl string) *template.Template {
	keyName := path.Join(this.dir, tpl)
	return this.templates[keyName]
}

// render template file with data and nested
func (this *View) Render(tpl string, data map[string]interface {}, nested []string) (string, error) {
	tp, e := this.Create(tpl, nested)
	if e != nil {
		return "", e
	}
	var byte bytes.Buffer
	e = tp.ExecuteTemplate(&byte, path.Base(tpl), data)
	if e != nil {
		return "", e
	}
	return byte.String(), nil
}

// add template func in global, must do before app.Run
func (this *View) Func(name string, f interface {}) {
	this.funcMap[name] = f
}

//---------------------------------

// create new view in directory
func NewView(dir string) *View {
	v := &View{}
	v.dir = dir
	v.IsCache = false
	v.templates = make(map[string]*template.Template)
	v.funcMap = make(template.FuncMap)
	v.funcMap["raw"] = func(str string) template.HTML {
		return template.HTML(str)
	}
	return v
}
